<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // Set.delete(2)解决
      let activeEffect;
      let effectStack = [];
      function effect(fn, options) {
        const effectFn = () => {
          cleanup(effectFn);
          activeEffect = effectFn;
          effectStack.push(effectFn);
          const res = fn();
          effectStack.pop();
          activeEffect = effectStack[effectStack.length - 1];
          return res;
        };
        effectFn.options = options;
        effectFn.deps = [];
        if (!options || !options.lazy) {
          effectFn();
        }
        return effectFn;
      }
      function cleanup(effectFn) {
        for (let i = 0; i < effectFn.deps.length; i++) {
          const deps = effectFn.deps[i];
          deps.delete(effectFn);
        }
        effectFn.deps.length = 0;
      }
      const bucket = new WeakMap();

      const jobQuene = new Set();
      const p = Promise.resolve();

      let isFlushing = false;
      function flushJob() {
        if (isFlushing) return;
        isFlushing = true;
        p.then(() => {
          jobQuene.forEach((job) => {
            job();
          });
        }).finally(() => {
          isFlushing = false;
        });
      }

      let temp1, temp2;
      function computed(getter) {
        let value;
        let dirty = true;
        const effectFn = effect(getter, {
          lazy: true,
          scheduler() {
            dirty = true;
          },
        });

        const obj = {
          get value() {
            // 只有dirty为脏时才计算值，并将得到的值缓存在value中
            if (dirty) {
              value = effectFn();
              // 将dirty值设置为false，下一次访问只用缓存中的值
              dirty = false;
            }
            return value;
          },
        };

        return obj;
      }
      const reactiveMap = new Map();
      let ITERATE_KEY = Symbol();
      function reactive(obj) {
        const existProxy = reactiveMap.get(obj);
        if (existProxy) {
          return existProxy;
        }
        const proxy = createReactive(obj);
        reactiveMap.set(obj, proxy);
        return proxy;
      }
      function shallowReactive(obj) {
        return createReactive(obj, true);
      }
      function readonly(obj) {
        return createReactive(obj, false, true);
      }

      const arrayInstrumentations = {};
      ["includes", "indexOf", "lastIndexOf"].forEach((method) => {
        const originMethod = Array.prototype[method];
        arrayInstrumentations[method] = function (...args) {
          let res = originMethod.apply(this, args);
          if (res === false || res === -1) {
            res = originMethod.apply(this.raw, args);
          }
          return res;
        };
      });
      let shouldTrack = true;
      ["push", "pop", "unshift", "shift"].forEach((method) => {
        const originMethod = Array.prototype[method];
        arrayInstrumentations[method] = function (...args) {
          shouldTrack = false;
          let res = originMethod.apply(this, args);
          shouldTrack = true;
          return res;
        };
      });

      const mutableInstrumentations = {
        add(key) {
          // 通过.raw属性来访问原始对象
          const target = this.raw;
          // 判断值在不在
          const hadKey = target.has(key);
          // 使用原始对象执行add方法
          const rawKey = key.raw || key
          const res = target.add(rawKey);
          // 触发trigger响应，指定操作类型为ADD
          if (!hadKey) {
            trigger(target, key, "ADD");
          }
          return res;
        },
        delete(key) {
          // 通过.raw属性来访问原始对象
          const target = this.raw;
          // 判断值在不在
          const hadKey = target.has(key);
          // 使用原始对象执行delete方法
          const res = target.delete(key);
          // 值不在时才去触发trigger响应，指定操作类型为DELETE
          if (hadKey) {
            trigger(target, key, "DELETE");
          }
          return res;
        },
        get (key) {
          const target = this.raw;
          const hadKey = target.has(key);
          // 追踪key建立响应响应的联系
          track(target, key);
          if (hadKey) {
            const res = target.get(key);
            // 如果res是对象，则继续执行reactive递归生成响应式数据
            return typeof res === "object" ? reactive(res) : res;
          }
        },
        set (key, value) {
          // 原始对象
          const target = this.raw;
          // 判断读取的key是否存在
          const had = target.has(key)
          // 旧值
          const oldVal = target[key]
          // target.set(key, value) // 这样赋值就是变量污染
          const rawValue = value.raw || value
          target.set(key, rawValue)
          // 触发trigger响应，指定操作类型为ADD
          if (!had) {
            // 新增
            trigger(target, key, "ADD");
          } else if (oldVal !== value && (oldVal === oldVal && value === value)) {
            // 修改
            trigger(target, key, "SET");
          }
        }
      };

      let map = new Map([
        ['key', 1],
        ['name', 2],
      ]);
      let proxy = reactive(map);
      effect(() => {
        console.log(proxy.get('key'), "proxy1.key");
      });
      // proxy.set('key', 2);

      let map2 = new Map();
      let p1 = reactive(map2)
      let p2 = reactive(map2)
      // 为p1设置一个键值对
      p1.set('p2', p2)
      effect(() => {
        console.log(map2.get('p2').size, 'map2.get p2 . size');
      })

      let set = new Set([1,2,3])
      let p3 = reactive(set)
      let p4 = reactive(set)
      p3.add(p4)
      function createReactive(obj, isShallow = false, isReadonly = false) {
        return new Proxy(obj, {
          get(target, key, receiver) {
            if (key === "raw") {
              return target;
            }
            // 针对Set数据结构的拦截
            if (key === "size") {
              track(target, ITERATE_KEY);
              return Reflect.get(target, key, target);
            }
            // delete走这里
            return mutableInstrumentations[key];
          },
          set(target, key, newVal, receiver) {
            // 只读
            if (isReadonly) {
              console.warn(`属性 ${key} 是只读的`);
              return true;
            }
            // 旧值
            let oldVal = target[key];
            let type = Array.isArray(target)
              ? Number(key) < target.length
                ? "SET"
                : "ADD"
              : Object.prototype.hasOwnProperty.call(target, key)
              ? "SET"
              : "ADD";
            // target[key] = newVal
            const res = Reflect.set(target, key, newVal, receiver);
            // 普通对象和receiver指向的raw相等
            if (target === receiver.raw) {
              if (
                oldVal !== newVal &&
                (!Number.isNaN(oldVal) || !Number.isNaN(newVal))
              ) {
                trigger(target, key, type, newVal);
              }
            }
            return true;
          },
          deleteProperty(target, key) {
            // 只读
            if (isReadonly) {
              console.warn(`属性 ${key} 是只读的`);
              return true;
            }
            // 检查被操作的属性是否是对象自己的属性
            let isOwnKey = Object.prototype.hasOwnProperty.call(target, key);
            // 删除操作
            let deleteRes = Reflect.deleteProperty(target, key);
            // 是自己的key，并且删除成功
            if (isOwnKey && deleteRes) {
              trigger(target, key, "DELETE");
            }
          },
          ownKeys(target) {
            track(target, Array.isArray(target) ? "length" : ITERATE_KEY);
            return Reflect.ownKeys(target);
          },
        });
      }

      function track(target, key) {
        if (!activeEffect || !shouldTrack) return;
        let depsMap = bucket.get(target);
        if (!depsMap) {
          bucket.set(target, (depsMap = new Map()));
        }
        let deps = depsMap.get(key);
        if (!deps) {
          depsMap.set(key, (deps = new Set()));
        }
        deps.add(activeEffect);
        activeEffect.deps.push(deps);
      }

      function trigger(target, key, type, newVal) {
        let depsMap = bucket.get(target);
        if (!depsMap) return;
        // 取得与key相关联的副作用函数
        const effects = depsMap.get(key);
        const effectsToRun = new Set();
        // 将与key相关联的副作用函数添加到effectsToRun
        effects &&
          effects.forEach((effectFn) => {
            if (effectFn !== activeEffect) {
              effectsToRun.add(effectFn);
            }
          });

        // 操作类型是ADD的时候，把length对应的副作用函数取出来，加入到effectsToRun中拿出来执行
        if (Array.isArray(target) && type === "ADD") {
          const lengthOfEffects = depsMap.get("length");
          lengthOfEffects &&
            lengthOfEffects.forEach((effectFn) => {
              if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn);
              }
            });
        }
        if (type === "ADD" || type === "DELETE") {
          // 取得与ItERATE_KEY关联的副作用函数
          const iterateEffects = depsMap.get(ITERATE_KEY);
          // 将与ITERATE_KEY相关联的副作用函数添加到effectsToRun
          iterateEffects &&
            iterateEffects.forEach((effectFn) => {
              if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn);
              }
            });
        }
        // 如果操作目标是数组，并且修改了数组的key属性
        if (Array.isArray(target) && key === "length") {
          depsMap.forEach((effects, effectKey) => {
            if (effectKey >= newVal) {
              effects.forEach((effectFn) => {
                if (effectFn !== activeEffect) {
                  effectsToRun.add(effectFn);
                }
              });
            }
          });
        }
        effectsToRun &&
          effectsToRun.forEach((effectFn) => {
            if (effectFn.options && effectFn.options.scheduler) {
              effectFn.options.scheduler(effectFn);
            } else {
              effectFn();
            }
          });
      }

      function watch(source, cb, options = {}) {
        let getter;
        if (typeof source === "function") {
          getter = source;
        } else {
          getter = () => traverse(source);
        }
        let newVal, oldVal;
        let cleanup; // 存储用户注册过的过期回调
        function onInvalidate(fn) {
          // 过期回调存储在cleanup中
          cleanup = fn;
        }
        const job = function () {
          newVal = effectFn();
          // 调用回调函数cb之前，先调用过期回调
          if (cleanup) {
            cleanup();
          }
          // 将onInvalidate作为回调函数的第三个参数，用户可以调用
          cb(newVal, oldVal, onInvalidate);
          oldVal = newVal;
        };
        const effectFn = effect(() => getter(), {
          scheduler() {
            if (options.flush === "post") {
              const p = Promise.resolve();
              p.then(job);
            } else {
              job();
            }
          },
        });

        if (options.immediate) {
          job();
        } else {
          // 手动调用副作用函数，拿到旧值
          oldVal = effectFn();
        }
      }

      function traverse(value, seen = new Set()) {
        // 如果数据是原始数据类型 或者已经侦听过，则return，防止死循环
        if (typeof value !== "object" || value === null || seen.has(value))
          return;
        // 将数据添加到seen里面
        seen.add(value);

        // 暂不考虑数组等其他数据结构,假设就是对象
        for (const key in value) {
          traverse(value[key], seen);
        }
        return value;
      }
      // watch(() => obj.bar, (newVal, oldVal) => {
      //     console.log(newVal, oldVal, 'newVal, oldVal数据变化了');
      // }, {
      //     immediate: true,
      //     flush: 'post'
      // })

      let finalData = 0;
      let count = 0;
      // watch(() => obj.bar, async (newVal, oldVal) => {
      //     const res = await request({url: '/path/to/request'})
      //     finalData = res
      // })

      // watch(() => obj.bar, async (newVal, oldVal, onInvalidate) => {
      //     // 定义一个标志，判断当前副作用函数是否过期，默认为false-没过期
      //     let expired = false

      //     onInvalidate(() => {
      //         // 调用onInvalidate函数，注册一个回调，当前副作用函数过期则将exipred改为true
      //         expired = true
      //     })

      //     const res = await new Promise((resolve, reject) => {
      //         setTimeout(() => {
      //             resolve(newVal * 2)
      //         }, 1000)
      //     })
      //     // 只有没有过期才能进行赋值
      //     if (!expired) {
      //         finalData = res
      //         console.log(`Updated value: ${res}`);
      //     } else {
      //         console.log('The effect was invalidated');
      //     }
      // })
    </script>
  </body>
</html>
